<?php

class UserBase extends Logger {
  
  protected static $_instance = null;
  protected $data = array();
  protected $metadata = array();
  protected $db = false;
  protected $logged = false;
  protected $login_url = "user/login";

  protected function __construct() {
    $this->restoreFromSession();
  }

  public static function getInstance(){
    if(null !== static::$_instance){
      return static::$_instance;
    }
    static::$_instance = new static();
    return static::$_instance;
  }

  public function restoreFromSession() {
    $user = Session::get('_current_user');
    if(!empty($user)){
      $this->data($user['data']);
      $this->metadata($user['metadata']);
    }
  }

  public function saveIntoSession() {
    $user = array('data' => $this->data,
                  'metadata' => $this->metadata);
    Session::set('_current_user', $user);
  }

  public function db($connection = NULL){
    // setea o devuelve la conexion a usar
    // la conexion debe ser un objeto de la clase DB
    // o una cadena que exista entre las "named connections" configuradas
    // por defecto se usa la conexion llamada "conexion"
    // especificada en la configuracion Config::get("conexion");

    if(is_null($connection)) {
      if(!$this->db) {
        $this->db = DB::getNamedInstance(Config::get('conexion', 'local'));
      }
      return $this->db;
    }

    if(is_string($connection)){
      $this->db = DB::getNamedInstance($connection);
    } elseif(!get_class($connection) == "DB" && !is_subclass_of($connection, "DB")) {
      throw new Exception("Solo puede usar conexiones que sean instancias de la clase DB o subclases.");
    }

    $this->db = $connection;
    return $this;
  }

  /*
  public function findById($id){
    $this->data($this->db()->executeOrException("SELECT * FROM user WHERE id = :id", array(":id" => $id))->fetch(PDO::FETCH_ASSOC));
    $this->logged = false;
    return $this;
  }
   */

  public function login($user, $pass, $redirect = null){
    $data = $this->db()->executeOrException(
            'SELECT * FROM user WHERE email = :user AND password = :pass',
            array(
                 ":user" => $user,
                 ":pass" => self::hash($pass)
                  )
            )->fetch(PDO::FETCH_ASSOC);

    if(!empty($data)) {
      $this->Inspect($data);
      $this->data($data);
      $this->isLogged(true);

      if(!empty($redirect)){
        $redirect = "http://" . $_SERVER['HTTP_HOST'] . '/'. HTMLHelper::removeLeadingSlash(base64_decode($redirect));
        header("Location: " . $redirect);
      }
      return $this;
    } else {
      return false;
    }
  }

  public function logout(){
    $this->data(array());
    $this->metadata(array());
    $this->isLogged(false);  // redundante pero por si acaso
  }

  public function forceLogin($target_url = null){
    if(!$this->isLogged()){
      $target_url = empty($target_url)?Config::get('base_url','/'):$target_url;
      header("Location: ".Config::get('base_url','/').$this->login_url . "?r=" . base64_encode($target_url));
    } else {
      return true;
    }
  }

  public function hash($string){
    return md5(md5($string."some_salt")."some_other_salt");
  }

  public function isLogged($logged = false){
    if(!$logged) {
      // act as a getter
      return $this->metadataItem('logged');
    } else {
      // act as a setter
      $this->metadataItem('logged', $logged);
      return $this;
    }
  }

  public function data($data = null){

    if(is_null($data)) {
      return $this->data;
    } else {
      $this->data = $data;
      $this->saveIntoSession();
      return $this;
    }

  }

  public function dataItem($item, $value = null){

    if(is_null($value)){
      // act as a getter
      if(isset($this->data) && isset($this->data[$item])){
        return $this->data[$item];
      } else {
        return null;
      }
    } else {
      // act as a setter
      if(!isset($this->data)) $this->data($array());
      $this->data[$item] = $value;
      $this->saveIntoSession();
    }

  }

  public function metadataItem($item, $value = null){

    if(is_null($value)){
      // act as a getter
      if(isset($this->metadata) && isset($this->metadata[$item])){
        return $this->metadata[$item];
      } else {
        return null;
      }
    } else {
      // act as a setter
      if(!isset($this->metadata)) $this->metadata($array());
      $this->metadata[$item] = $value;
      $this->saveIntoSession();
    }

  }

  public function metadata($data = null){

    if(is_null($data)) {
      return $this->metadata;
    } else {
      $this->metadata = $data;
      $this->saveIntoSession();
      return $this;
    }

  }
  
  public function retrievePassToken($mail, $date = null){
    // dado un $mail, genera el link para resetear contraseña
    
    $date = is_null($date)?date('Y-m-d'):$date;
    
    $data = $this->db()->executeOrException(
            'SELECT * FROM user WHERE email = :mail',
            array(
                 ":mail" => $mail
                  )
            )->fetch(PDO::FETCH_ASSOC);
            
    if(!empty($data) && isset($data['password'])) {
        $token = md5(md5($data['password'] . $date . 'suca'));
    } else {
        // invalid token to avoid accidental validation
        $token = md5(md5(rand(1,1000) . date('Y-m-d H:i:s') . $date . 'suca'));
    }
    return $token;
  }
  
  public function resetPass($mail, $token, $newPass){
    // dado un $mail, verifica si el $link para recuperar contraseña es valido
    // en cuyo caso reemplaza la contraseña por $newPass
    if($token == $this->retrievePassToken($mail)){
      $this->savePass($mail, $newPass);
      return true;
    } else {
      return false;  
    }
  }
  
  public function savePass($mail, $newPass){
    // reemplaza por $newPass la contraseña del usuario identificado por $mail
    
    $user = Usuario::find(array('email' => $mail));
    $user->data['password'] = $newPass;
    $user->save();
  }
/*
  public function save(){
    if(empty($this->data)) return false;

    $keys = array_keys($this->data);
    try {
      $query =
      'INSERT INTO user ('. implode(', ', $keys ) .') VALUES (:'. implode(', :', $keys ) .')';

      //Inspect::view($query);

      $data = $this->db()->executeOrException(
              $query,
              $this->data
              )->fetch(PDO::FETCH_ASSOC);
    } catch (Exception $e) {
      $query = 'UPDATE user SET ';

      $fields = array();
      foreach($keys as $field){
        $fields[] = $field . " = :" . $field;
      }
      $query .= implode(", ", $fields) . " WHERE id = :id";

      //Inspect::view($query);

      $data = $this->db()->executeOrException(
              $query,
              $this->data
              )->fetch(PDO::FETCH_ASSOC);
    }

    if(!empty($data)) {
      $this->data = $data;
      $this->logged = true;
    }

    //Inspect::view($this->data);

    return $this;
  }

  public static function create($data = null){

    if(empty($data)) return false;
    
    $classname = __CLASS__;

    $new_user = new $classname();

    $keys = array_keys($data);

    $query =
    'INSERT INTO user ('. implode(', ', $keys ) .') VALUES (:'. implode(', :', $keys ) .')';

    //Inspect::view($query);

    $new_user->db()->executeOrException(
            $query,
            $data
            )->fetch(PDO::FETCH_ASSOC);
   
    $new_user->findById($new_user->db()->lastInsertId());

    //Inspect::view($new_user->data);

    return $new_user;
  }
 */

}